package com.google.code.jwebannotation;

import java.io.IOException;
import java.util.HashMap;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Servlet implementation class ControllerServlet
 */
public class SimpleFrameworkServlet extends HttpServlet {

	private Log log = LogFactory.getLog(SimpleFrameworkServlet.class);

	private static final long serialVersionUID = 1L;
	static HashMap<String, InternalWrapper> map;
	static InternalWrapper errorPage;
	private String beanAttributeName;

	protected void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		process(HttpMethod.GET, request, response);
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		process(HttpMethod.POST, request, response);
	}

	private void process(HttpMethod type, HttpServletRequest request, HttpServletResponse response)
			throws IOException {

		String pathName = request.getServletPath();
		if (pathName == null || pathName.equals("")) {
			pathName = request.getPathInfo();
		}

		log.trace("Using path '" + pathName + "'");

		InternalWrapper mw = map.get(pathName);
		if (mw != null) {
			if (mw.getHttpMethod().equals(type) || mw.getHttpMethod().equals(HttpMethod.ALL)) {
				try {
					if (mw.getBean() != null && !mw.getBean().equals(Object.class)) {
						log.debug("Fetching bean off request for class '" + mw.getBean() + "'");
						Object form = BeanFunctions.populate(mw.getBean(), request);
						log.debug("Bean set in request attribute '" + beanAttributeName + "'");
						if (request.getAttribute(beanAttributeName) == null) {
							request.setAttribute(beanAttributeName, form);
						} else {
							log.warn("Request attribute '" + beanAttributeName
									+ "' is not null. Cannot place form bean in request");
						}
					}

					log.trace("Invoking method '" + mw.getObjectMethod() + "'");
					String ret = (String) mw.getObjectMethod().invoke(mw.getObject(), request,
							response);
					log.trace("Method returned: " + ret);
					if (ret != null) {
						request.getRequestDispatcher(ret).forward(request, response);
					}
					return;
				} catch (Exception e) {
					if (errorPage == null)
						throw new RuntimeException(e);
					fireErrorHandler(request, response, e);
					return;
				}
			} else {
				log.debug("Method does not match annotation");
			}
		}
		response.sendError(404);
	}

	private void fireErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception ex) {
		log.trace("Firing error handler page");
		try {
			log.trace("Invoking method '" + errorPage.getObjectMethod() + "'");
			String s = (String) errorPage.getObjectMethod().invoke(errorPage.getObject(), request, response, ex);
			if (s != null){
				request.getRequestDispatcher(s).forward(request, response);
			}
		} catch (Exception e) {
			log.error("Error handler throw an exception", e);
		}

	}

	@Override
	public void init(ServletConfig config) throws ServletException {

		beanAttributeName = config.getInitParameter("BEAN_ATTRIBUTE_NAME");
		if (beanAttributeName == null)
			beanAttributeName = "form";
		log.info("Bean attribute name set to '" + beanAttributeName + "'");
	}

}
